Tutorial Setup Next.js + Prisma v6 + MySQL (Database Existing)
Dokumentasi ini merangkum proses setup dari nol: membuat project Next.js, menghubungkan ke database MySQL yang sudah ada menggunakan Prisma v6, sampai menampilkan data lewat route.
0. Kesimpulan Penting Sebelum Mulai
Sempat dicoba pakai Prisma v7, tapi ada beberapa perubahan besar di versi itu yang bikin proses jadi rumit untuk pemula:
| Isu di Prisma v7 | Penyebab |
|---|---|
PrismaClient tidak ter-export dari @prisma/client |
v7 mewajibkan custom output path, client tidak lagi otomatis masuk ke node_modules/@prisma/client |
new PrismaClient() error "Expected 1 arguments, got 0" |
v7 menghapus query engine internal, wajib pakai driver adapter (@prisma/adapter-mariadb, dll) |
datasourceUrl dianggap unknown property |
Opsi lama untuk override URL tidak berlaku lagi di v7 |
Keputusan: pakai Prisma v6 karena lebih stabil, masih pakai query engine bawaan (tidak perlu adapter), dan proses generate client jauh lebih simpel — cocok untuk belajar dari nol.
1. Buat Project Next.js
npx create-next-app@latest nama-project
Pilihan yang direkomendasikan:
- TypeScript → Yes
- App Router → Yes
src/directory → catat pilihanmu, ini menentukan lokasi file nanti (contoh di tutorial ini tidak pakaisrc/, langsungapp/di root)
cd nama-project
ls
2. Install Prisma v6 (Eksplisit — Jangan @latest)
Supaya tidak ke-pull v7 secara tidak sengaja:
npm install prisma@6 --save-dev
npm install @prisma/client@6
Verifikasi versi:
npx prisma -v
Pastikan hasilnya 6.x.x.
3. Inisialisasi Prisma dengan MySQL
npx prisma init --datasource-provider mysql
Perintah ini otomatis membuat:
prisma/schema.prismadenganprovider = "mysql"prisma.config.ts(fitur konfigurasi baru di Prisma).envdengan templateDATABASE_URL
Tentang prisma.config.ts — Prisma versi terbaru memindahkan sebagian konfigurasi (URL, path migrations, engine) ke file ini. Contoh isi yang valid:
import "dotenv/config";
import { defineConfig, env } from "prisma/config";
export default defineConfig({
schema: "prisma/schema.prisma",
migrations: {
path: "prisma/migrations",
},
engine: "classic",
datasource: {
url: env("DATABASE_URL"),
},
});
Selama ada import "dotenv/config" dan env("DATABASE_URL"), isi .env tetap terbaca normal. Pesan "Prisma config detected, skipping environment variable loading" saat menjalankan CLI bukan error.
4. Isi Connection String di .env
DATABASE_URL="mysql://user:password@localhost:3306/nama_database"
Ganti user, password, dan nama_database sesuai kredensial MySQL kamu.
5. Tarik Struktur Tabel yang Sudah Ada (Introspeksi)
Karena database sudah berisi tabel (misalnya users, role, rolecat, appmenunew, dll):
npx prisma db pull
Cek hasilnya:
cat prisma/schema.prisma
Prisma otomatis membaca seluruh tabel & relasi (foreign key) dari database dan menuliskannya sebagai model.
6. Generate Prisma Client
npx prisma generate
Penting: cek output-nya untuk tahu ke mana client di-generate, misalnya:
✔ Generated Prisma Client (6.x.x) to ./app/generated/prisma
Kalau generator client di schema kamu punya baris output custom, maka import Prisma Client tidak boleh dari @prisma/client, tapi dari path itu langsung.
7. Cek Alias @/ di tsconfig.json
cat tsconfig.json | grep -A 3 paths
Untuk project tanpa folder src/, pastikan:
"paths": {
"@/*": ["./*"]
}
8. Buat File Singleton Prisma Client
mkdir -p lib
// lib/prisma.ts
import { PrismaClient } from '../app/generated/prisma'
// sesuaikan path ini dengan lokasi output di schema.prisma kamu
const globalForPrisma = globalThis as unknown as {
prisma: PrismaClient | undefined
}
export const prisma = globalForPrisma.prisma ?? new PrismaClient()
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma
Kenapa pola singleton ini? Next.js dev mode melakukan hot-reload berkali-kali. Tanpa pola globalForPrisma, tiap reload akan membuat koneksi Prisma Client baru dan lama-lama menghabiskan koneksi database.
9. Buat Route untuk Menampilkan Data
Aturan Routing Next.js App Router (Penting!):
| Struktur | Hasil |
|---|---|
app/roles/page.tsx | ✅ jadi route /roles |
app/roles.tsx | ❌ tidak dikenali sebagai route sama sekali |
app/users/page.tsx | ✅ jadi route /users |
Route wajib berupa folder dengan file bernama persis page.tsx di dalamnya — bukan file lepas.
Contoh Route Sederhana — /users
mkdir -p app/users
// app/users/page.tsx
import { prisma } from '@/lib/prisma'
export default async function UsersPage() {
const users = await prisma.users.findMany({
select: {
id: true,
name: true,
email: true,
level: true,
},
orderBy: { id: 'desc' },
})
if (users.length === 0) {
return <p className="p-6 text-gray-500">Belum ada data user.</p>
}
return (
<div className="p-6">
<h1 className="text-2xl font-bold mb-4">Daftar User</h1>
<table className="w-full border-collapse">
<thead>
<tr className="border-b">
<th className="text-left p-2">ID</th>
<th className="text-left p-2">Nama</th>
<th className="text-left p-2">Email</th>
<th className="text-left p-2">Level</th>
</tr>
</thead>
<tbody>
{users.map((user) => (
<tr key={user.id.toString()} className="border-b">
<td className="p-2">{user.id.toString()}</td>
<td className="p-2">{user.name}</td>
<td className="p-2">{user.email}</td>
<td className="p-2">{user.level ?? '-'}</td>
</tr>
))}
</tbody>
</table>
</div>
)
}
⚠️ Catatan tipe data: kolom id bertipe BigInt di MySQL/Prisma. React/Next.js tidak bisa merender BigInt langsung di JSX — wajib .toString(), kalau tidak akan muncul error Do not know how to serialize a BigInt.
Contoh Route dengan Relasi — /roles
Model role berelasi ke rolecat dan appmenunew, jadi gunakan include:
mkdir -p app/roles
// app/roles/page.tsx
import { prisma } from '@/lib/prisma'
export default async function RolesPage() {
const roles = await prisma.role.findMany({
include: {
rolecat: true,
appmenunew: true,
},
orderBy: { idrole: 'desc' },
})
if (roles.length === 0) {
return <p className="p-6 text-gray-500">Belum ada data role.</p>
}
return (
<div className="p-6">
<h1 className="text-2xl font-bold mb-4">Daftar Role</h1>
<table className="w-full border-collapse">
<thead>
<tr className="border-b">
<th className="text-left p-2">ID</th>
<th className="text-left p-2">Kategori Role</th>
<th className="text-left p-2">Menu</th>
</tr>
</thead>
<tbody>
{roles.map((role) => (
<tr key={role.idrole} className="border-b">
<td className="p-2">{role.idrole}</td>
<td className="p-2">{role.rolecat?.rolecat ?? '-'}</td>
<td className="p-2">{role.appmenunew?.appmenunew ?? '-'}</td>
</tr>
))}
</tbody>
</table>
</div>
)
}
Field idrole bertipe Int, jadi tidak perlu .toString() seperti id di tabel users.
10. Jalankan Project
npm run dev
Akses di browser:
http://localhost:3000/users
http://localhost:3000/roles
Akses dari IP Lain / VPS (bukan localhost) — kalau muncul warning allowedDevOrigins, tambahkan di next.config.js:
module.exports = {
allowedDevOrigins: ['192.168.1.100'],
}
Lalu restart dev server.
Troubleshooting — Ringkasan Masalah yang Pernah Terjadi
| Error | Penyebab | Solusi |
|---|---|---|
| P1013: database string invalid, must start with postgresql:// | Schema masih default Postgres, padahal DB-nya MySQL | Set provider = "mysql", samakan format DATABASE_URL |
| Cannot find module '@/lib/prisma' | File belum dibuat, atau alias @/ tidak sesuai struktur folder |
Cek paths di tsconfig.json |
| Module '@prisma/client' has no exported member 'PrismaClient' | Client di-generate ke folder custom (output), bukan ke @prisma/client |
Import dari path output yang sebenarnya |
| Expected 1 arguments, but got 0 saat new PrismaClient() | Prisma v7 menghapus query engine internal, wajib driver adapter | Pakai Prisma v6 tanpa adapter |
| @prisma/client did not initialize yet | Belum pernah prisma generate, atau import path salah |
Jalankan npx prisma generate, sesuaikan import |
| GET /roles 404 | File tersimpan sebagai app/roles.tsx, bukan app/roles/page.tsx |
mkdir -p app/roles && mv app/roles.tsx app/roles/page.tsx |
| Do not know how to serialize a BigInt | Kolom id bertipe BigInt, tidak bisa langsung di-render JSX |
Tambahkan .toString() |
| Hydration mismatch warning di browser | Biasanya dari browser extension, bukan dari kode | Bisa diabaikan, cek di mode incognito |
Checklist Cepat Kalau Ada Masalah
npx prisma -v→ pastikan versi sesuai (v6 untuk tutorial ini)cat prisma/schema.prisma→ cekproviderdan barisoutputnpx prisma generate→ perhatikan baris "Generated Prisma Client ... to ..."- Import
PrismaClientdilib/prisma.tsharus mengarah ke lokasi hasil generate yang sebenarnya - Struktur route harus
app/nama-route/page.tsx, bukan file lepas - Restart dev server setiap habis mengubah
tsconfig.json,next.config.js, atau schema Prisma - Kolom bertipe
BigIntwajib.toString()sebelum dirender di JSX